{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Sine subtraction - MusicBricks Tutorial"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Introduction"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This tutorial will guide you through some tools for performing spectral analysis and synthesis using the Essentia library (http://www.essentia.upf.edu). \n",
    "\n",
    "This algorithm shows how to analyze the input signal, and resynthesize it again, allowing to apply new transformations directly on the spectral domain."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You should first install the Essentia library with Python bindings. Installation instructions are detailed here: http://essentia.upf.edu/documentation/installing.html . \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Processing steps\n",
    "We'll use essentia in streaming mode"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import essentia\n",
    "import essentia.streaming as es"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Define the parameters of the STFT workflow"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "params = {'frameSize': 2048, 'hopSize': 128, 'startFromZero': False, 'sampleRate': 44100, \\\n",
    "          'maxnSines': 100,'magnitudeThreshold': -74,'minSineDur': 0.02,'freqDevOffset': 10, \\\n",
    "          'freqDevSlope': 0.001}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Specify input and output audio filenames"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "inputFilename = 'singing-female.wav'\n",
    "outputFilename = 'singing-female-sinesubtraction.wav'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# create an audio loader and import audio file\n",
    "loader = es.MonoLoader(filename=inputFilename, sampleRate=44100)\n",
    "pool = essentia.Pool()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Define algorithm chain for frame-by-frame process: \n",
    "FrameCutter -> Windowing -> FFT -> SineModelAnal -> SineSubtraction -> OutFrames"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# algorithm instantation\n",
    "fcut = es.FrameCutter(frameSize=params['frameSize'], hopSize=params['hopSize'], startFromZero=False)\n",
    "w = es.Windowing(type=\"blackmanharris92\")\n",
    "fft = es.FFT(size=params['frameSize'])\n",
    "smanal = es.SineModelAnal(\n",
    "    sampleRate=params['sampleRate'],\n",
    "    maxnSines=params['maxnSines'],\n",
    "    magnitudeThreshold=params['magnitudeThreshold'],\n",
    "    freqDevOffset=params['freqDevOffset'],\n",
    "    freqDevSlope=params['freqDevSlope']\n",
    "    )\n",
    "subtrFFTSize = int(min(params['frameSize'] / 4, 4 * params['hopSize']))\n",
    "smsub = es.SineSubtraction(\n",
    "    sampleRate=params['sampleRate'],\n",
    "    fftSize=subtrFFTSize,\n",
    "    hopSize=params['hopSize']\n",
    "    )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we set the algorithm network and store the processed audio samples in the output file"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "# analysis\n",
    "loader.audio >> fcut.signal\n",
    "fcut.frame >> w.frame\n",
    "w.frame >> fft.frame\n",
    "fft.fft >> smanal.fft\n",
    "smanal.magnitudes >> (pool, 'magnitudes')\n",
    "smanal.frequencies >> (pool, 'frequencies')\n",
    "smanal.phases >> (pool, 'phases')\n",
    "# subtraction\n",
    "fcut.frame >> smsub.frame\n",
    "smanal.magnitudes >> smsub.magnitudes\n",
    "smanal.frequencies >> smsub.frequencies\n",
    "smanal.phases >> smsub.phases\n",
    "smsub.frame >> (pool, 'frames')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally we run the process that will store the output audio frames in the Pool."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "essentia.run(loader)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next we store the output audio samples in a WAV file.\n",
    "We first prepare the audio writing network:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<essentia.streaming._StreamConnector at 0x1122f8d90>"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# get audio samples\n",
    "outaudio = pool['frames'].flatten()\n",
    "# instantiate audio writer and vector input\n",
    "awrite = es.MonoWriter (filename=outputFilename, sampleRate=params['sampleRate'])\n",
    "outvector = es.VectorInput(outaudio)\n",
    "\n",
    "# set algorithm network\n",
    "outvector.data >> awrite.audio"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally we run the process for to store the output frames."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": true
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[wav @ 0x7f9a0400d800] Using AVStream.codec to pass codec parameters to muxers is deprecated, use AVStream.codecpar instead.\n"
     ]
    },
    {
     "ename": "RuntimeError",
     "evalue": "MonoWriter: error writing to audio file: Error while encoding audio frame",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mRuntimeError\u001b[0m                              Traceback (most recent call last)",
      "\u001b[1;32m/Users/xavierlizarraga/dev/python/repos/mir/my_fork/essentia/src/examples/python/musicbricks-tutorials/3-sine_subtraction.ipynb Cell 21'\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> <a href='vscode-notebook-cell:/Users/xavierlizarraga/dev/python/repos/mir/my_fork/essentia/src/examples/python/musicbricks-tutorials/3-sine_subtraction.ipynb#ch0000022?line=0'>1</a>\u001b[0m essentia\u001b[39m.\u001b[39;49mrun(outvector)\n",
      "File \u001b[0;32m~/dev/python/repos/mir/test_essentia/env/lib/python3.9/site-packages/essentia/__init__.py:148\u001b[0m, in \u001b[0;36mrun\u001b[0;34m(gen)\u001b[0m\n\u001b[1;32m    <a href='file:///Users/xavierlizarraga/dev/python/repos/mir/test_essentia/env/lib/python3.9/site-packages/essentia/__init__.py?line=145'>146</a>\u001b[0m \u001b[39mif\u001b[39;00m \u001b[39misinstance\u001b[39m(gen, VectorInput) \u001b[39mand\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mlist\u001b[39m(gen\u001b[39m.\u001b[39mconnections\u001b[39m.\u001b[39mvalues())[\u001b[39m0\u001b[39m]:\n\u001b[1;32m    <a href='file:///Users/xavierlizarraga/dev/python/repos/mir/test_essentia/env/lib/python3.9/site-packages/essentia/__init__.py?line=146'>147</a>\u001b[0m     \u001b[39mraise\u001b[39;00m EssentiaError(\u001b[39m'\u001b[39m\u001b[39mVectorInput is not connected to anything...\u001b[39m\u001b[39m'\u001b[39m)\n\u001b[0;32m--> <a href='file:///Users/xavierlizarraga/dev/python/repos/mir/test_essentia/env/lib/python3.9/site-packages/essentia/__init__.py?line=147'>148</a>\u001b[0m \u001b[39mreturn\u001b[39;00m _essentia\u001b[39m.\u001b[39;49mrun(gen)\n",
      "\u001b[0;31mRuntimeError\u001b[0m: MonoWriter: error writing to audio file: Error while encoding audio frame"
     ]
    }
   ],
   "source": [
    "essentia.run(outvector)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "interpreter": {
   "hash": "2146b6db258b20f3c29b7f1d7771ef33b30f307033b5e0e893928c1a552d47a9"
  },
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
